#ifndef _CODELETS_H
#define _CODELETS_H

#include <cuda.h>

/*
	x, y	- the x and y dimensions of the image (in pixels)
	kx ky	- the kernel's x and y dimensions
	input	- input data
	output	- output data
	kernel	- kernel data
*/
void __global__ convolve2DGPU(int x, int y, int kx, int ky, BYTE* input, BYTE* output, float *kernel)
{
	//int col = blockIdx.x * blockDim.x + threadIdx.x;	
	//int row = blockIdx.y * blockDim.y + threadIdx.y;

	int e =	(blockDim.x * threadIdx.y + threadIdx.x) + (blockIdx.x * blockDim.x * blockDim.y) + blockIdx.y * (blockDim.x * blockDim.y * gridDim.x);

//    int e = row * x + col;	// e is the normalized index
//    int kx_mid = kx / 2;	// point to the center of the kernel in the x direction
//    int ky_mid = ky / 2;	// point to the center of the kernel in the y direction
//
//    double sum = 0;
//    for (int ki = 0; ki < ky; ++ki)
//    {
//        int ny = ki - ky_mid;	// normalized y
//        for (int kj = 0; kj < kx; ++kj)
//        {
//            int i_index = e + (ny * x) + (kj - kx_mid);		// index of the input element to be multiplied
//            int k_index = ((ky * kx) - 1) - (ki*kx) - kj;
//
//            if (i_index >= 0 || i_index < x*y)
//            {
//                sum += input[i_index] * kernel[k_index];
//            }
//        }
//    }

    output[e] = e;
//    output[e] = sum;
}

/*
	x, y	- the x and y dimensions of the image (in pixels)
	kx ky	- the kernel's x and y dimensions
	input	- input data
	output	- output data
	kernel	- kernel data
*/
void convolve2DCPU(int x, int y, int kx, int ky, BYTE* input, BYTE* output, float *kernel)
{
    for (int i = 0; i < y; ++i)
    {
        for (int j = 0; j < x; ++j)
        {
            int e = i * x + j;	// e is the normalized index
            int kx_mid = kx >> 1;	// point to the center of the kernel in the x direction
            int ky_mid = ky >> 1;	// point to the center of the kernel in the y direction

            double sum = 0;
            for (int ki = 0; ki < ky; ++ki)
            {
                int ny = ki - ky_mid;	// normalized y
                for (int kj = 0; kj < kx; ++kj)
                {
                    int i_index = e + (ny * x) + (kj - kx_mid);		// index of the input element to be multiplied
                    int k_index = ((ky * kx) - 1) - (ki*kx) - kj;

                    if (i_index >= 0 || i_index < x*y)
                    {
                        sum += input[i_index] * kernel[k_index];
                    }
                }
            }

            output[e] = sum;
        }
    }
}

void convolve2D(int xLength, int yLength, BYTE* input, BYTE* output, float *kernel)
{
    for (int i = 0; i < yLength; ++i)
    {
        for (int j = 0; j < xLength; ++j)
        {
            int sum = 0;

            //
            int i1 = i-1;
            int i3 = i+1;

            int j1 = j-1;
            int j3 = j+1;

            // boundary condition check, wrap around if necessary
            if (i1 < 0)
                i1 = yLength - 1;
            if (i3 > yLength - 1)
                i3 = 0;
            if (j1 < 0)
                j1 = xLength - 1;
            if (j3 > xLength -1)
                j3 = 0;

            // one-dimensional indices for input array
            int index0 = i1 * xLength + j1;
            int index1 = i1 * xLength + j;
            int index2 = i1 * xLength + j3;
            int index3 = i * xLength + j1;
            int index4 = i * xLength + j;
            int index5 = i * xLength + j3;
            int index6 = i3 * xLength + j1;
            int index7 = i3 * xLength + j;
            int index8 = i3 * xLength + j3;

            // now take the dot product, store it in sum and place it in an out-of-place array
            // dot product between reflected kernel values, e.g.: input_index1 * kernel_index9
            sum += (input[index0] * kernel[8] + input[index1] * kernel[7] +input[index2] * kernel[6] +  input[index3] * kernel[5] + input[index4] * kernel[4] + input[index5] * kernel[3] + input[index6] * kernel[2] + input[index7] * kernel[1] + input[index8] * kernel[0]);

            output[i * xLength + j] = sum;
        }
    }
}

#endif
